/*****************************************************************************
 *  ENTROPY - emerging network to reduce orwellian potency yield
 *
 *  Copyright (C) 2002 Juergen Buchmueller <pullmoll@stop1984.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundation,
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 *	$Id: key.c,v 1.7 2005/07/25 05:12:59 pullmoll Exp $
 *****************************************************************************/
#include "key.h"
#include "uri.h"
#include "crypt.h"
#include "tools.h"
#include "logger.h"

/**
 * @brief Log2 of the smallest value larger than size
 *
 * Return the log2 of the smallest size_t larger
 * than the given size, e.g. log2size(200) = 8 (2^8 = 256)
 *
 * @param size size to logarithmize
 *
 * @returns log2 of size
 */
uint8_t log2size(size_t size)
{
	uint8_t log2 = 0;
	while (size > (1ul << log2))
		log2++;
	return log2;
}

/**
 * @brief Create content hash key for a byte array
 *
 * Create a content hash key for the given buffer and size.
 *
 * @param key pointer to the receiving chkey_t
 * @param buff byte array of data to hash
 * @param size size of byte array to has
 *
 * @result zero on success
 */
int key_chk(chkey_t *key, const void *buff, size_t size)
{
	key_state_t ks;
	FUN("key_chk");

	sha1_init(&ks.ss);
	sha1_append(&ks.ss, buff, size);
	sha1_finish(&ks.ss, &key->sha1);

	key->log2size = log2size(size);

	key->type[0] = MSB(K_CHK);
	key->type[1] = LSB(K_CHK);

	ek5_init(&ks.es);
	ek5_append(&ks.es, buff, size);
	ek5_finish(&ks.es, &key->ek5);

	key->padding = 0;

	return 0;
}

/**
 * @brief Return base64 string for a key
 *
 * Return a string representation for a key.
 * The bytes are base64 encoded.
 *
 * @param key pointer to a key
 *
 * @result pointer to a string containing the base64 encoded key
 */
const char *key_str(const chkey_t *key)
{
#undef	BUFNUM
#undef	BUFSIZE
#define	BUFNUM	16
#define	BUFSIZE	(KEY1SIZE+1+KEY2SIZE+1)
	static char *buff[BUFNUM] = {
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
	};
	static int which = 0;
	uint8_t key1[SHA1SIZE+1+2];
	uint8_t key2[EK5SIZE];

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);
	buff[which] = (char *)xcalloc(BUFSIZE, sizeof(char));

	memcpy(&key1[0], &key->sha1, SHA1SIZE);
	memcpy(&key1[SHA1SIZE], &key->log2size, 1);
	memcpy(&key1[SHA1SIZE+1], &key->type, 2);
	memcpy(&key2[0], &key->ek5, EK5SIZE);

	if (key->type[0] == MSB(K_CHK) && key->type[1] == LSB(K_CHK)) {
		pm_snprintf(buff[which], BUFSIZE, "%s,%s",
			base64_encode(key1, SHA1SIZE+1+2),
			base64_encode(key2, EK5SIZE));
	} else if (key->type[0] == MSB(K_BIT) && key->type[1] == LSB(K_BIT)) {
		pm_snprintf(buff[which], BUFSIZE, "%s,%s",
			base64_encode(key1, SHA1SIZE+1+2),
			base64_encode(key2, EK5SIZE));
	} else if (key->type[0] == MSB(K_SSK_P) && key->type[1] == LSB(K_SSK_P)) {
		pm_snprintf(buff[which], BUFSIZE, "%s",
			base64_encode(key1, SHA1SIZE+1+2));
	} else if (key->type[0] == MSB(K_SSK_S) && key->type[1] == LSB(K_SSK_S)) {
		pm_snprintf(buff[which], BUFSIZE, "%s",
			base64_encode(key1, SHA1SIZE));
	} else {
		pm_snprintf(buff[which], BUFSIZE, "%s",
			base64_encode(key1, SHA1SIZE+1+2));
	}

	return buff[which];
}

/**
 * @brief Return fully qualified representation for a key.
 *
 * Return a fully qualified representation for a key, including
 * the intial key type BIT@ (internal chunks), CHK@ (content
 * hash key) or SSK@ (public and private keys).
 * Unknown key types are prepended by the four digit hex value
 * of the type.
 *
 * @param key pointer to a key
 *
 * @result pointer to a string containing the fully qualified base64 key
 */
const char *key_long(const chkey_t *key)
{
#undef	BUFNUM
#undef	BUFSIZE
#define	BUFNUM	16
#define	BUFSIZE	(8+KEY1SIZE+1+KEY2SIZE+1)
	static char *buff[BUFNUM] = {
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
	};
	static int which = 0;
	uint8_t key1[SHA1SIZE+1+2];
	uint8_t key2[EK5SIZE];

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);
	buff[which] = (char *)xcalloc(BUFSIZE, sizeof(char));

	memcpy(key1, key->sha1.digest, SHA1SIZE);
	key1[SHA1SIZE] = key->log2size;
	key1[SHA1SIZE+1] = key->type[0];
	key1[SHA1SIZE+2] = key->type[1];
	memcpy(key2, key->ek5.digest, EK5SIZE);

	if (key->type[0] == MSB(K_CHK) && key->type[1] == LSB(K_CHK)) {
		pm_snprintf(buff[which], BUFSIZE,
			"CHK@%s,%s",
			base64_encode(key1, SHA1SIZE+1+2),
			base64_encode(key2, EK5SIZE));
	} else if (key->type[0] == MSB(K_BIT) && key->type[1] == LSB(K_BIT)) {
		pm_snprintf(buff[which], BUFSIZE,
			"BIT@%s,%s",
			base64_encode(key1, SHA1SIZE+1+2),
			base64_encode(key2, EK5SIZE));
	} else if (key->type[0] == MSB(K_SSK_P) && key->type[1] == LSB(K_SSK_P)) {
		pm_snprintf(buff[which], BUFSIZE,
			"SSK@%s",
			base64_encode(key1, SHA1SIZE+1+2));
	} else if (key->type[0] == MSB(K_SSK_S) && key->type[1] == LSB(K_SSK_S)) {
		pm_snprintf(buff[which], BUFSIZE,
			"SSK@%s",
			base64_encode(key1, SHA1SIZE));
	} else {
		pm_snprintf(buff[which], BUFSIZE,
			"0x%02x%02x@%s,%s",
			key->type[0], key->type[1],
			base64_encode(key1, SHA1SIZE+1+2),
			base64_encode(key2, EK5SIZE));
	}

	return buff[which];
}

/**
 * @brief Return shortened representation for a key.
 *
 * Return a short string representation for a key for debugging,
 * printing and hiding full keys from logs.
 *
 * @param key pointer to a key
 *
 * @result pointer to a string containing the shortened key
 */
const char *key_short(const chkey_t *key)
{
#undef	BUFNUM
#undef	BUFSIZE
#define	BUFNUM	16
#define	BUFSIZE	(8+8+3+1)
	static char *buff[BUFNUM] = {
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
	};
	static int which = 0;

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);
	buff[which] = (char *)xcalloc(BUFSIZE, sizeof(char));

	switch ((key->type[0] << 8) + key->type[1]) {
	case K_CHK:
		pm_snprintf(buff[which], BUFSIZE,
			"CHK@%-8.8s...",
			base64_encode(key->sha1.digest, 7));
		break;
	case K_KSK:
		pm_snprintf(buff[which], BUFSIZE,
			"KSK@%-8.8s...",
			base64_encode(key->sha1.digest, 7));
		break;
	case K_SSK_P:
	case K_SSK_S:
		pm_snprintf(buff[which], BUFSIZE,
			"SSK@%-8.8s...",
			base64_encode(key->sha1.digest, 7));
		break;
	case K_BIT:
		pm_snprintf(buff[which], BUFSIZE,
			"BIT@%-8.8s...",
			base64_encode(key->sha1.digest, 7));
		break;
	default:
		pm_snprintf(buff[which], BUFSIZE,
			"0x%02x%02x@%-8.8s...",
			key->type[0], key->type[1],
			base64_encode(key->sha1.digest, 7));
	}

	return buff[which];
}

/**
 * @brief Check and decode a base64 encoded key.
 *
 * Convert a base64 encoded key into the internal representation.
 * The string uri may be prepended by a protocol specifier
 * URI_PROTO or the alternative URI_PROTO_ALT.
 *
 * @param key pointer to a key to be set
 * @param uri pointer to a string containing the key in base64 representation.
 *
 * @result zero on success, non-zero on error (decoding base64)
 */
int str_key(chkey_t *key, const char *uri)
{
	uint8_t key1[SHA1SIZE+1+2];
	uint8_t key2[EK5SIZE];
	size_t size;
	int rc = 0;
	FUN("str_key");

	memset(key1, 0, sizeof(key1));
	memset(key2, 0, sizeof(key2));

	if (0 == strncmp(uri, URI_PROTO, strlen(URI_PROTO))) {
		uri += strlen(URI_PROTO);
	} else if (0 == strncmp(uri, URI_PROTO_ALT, strlen(URI_PROTO_ALT))) {
		uri += strlen(URI_PROTO_ALT);
	}
	if (0 == strncmp(uri, URI_CHK, strlen(URI_CHK))) {
		uri += strlen(URI_CHK);
	}
	if (0 == strncmp(uri, URI_SSK, strlen(URI_SSK))) {
		uri += strlen(URI_SSK);
	}

	size = SHA1SIZE + 1 + 2;
	if (0 != (rc = base64_decode(key1, &size, uri))) {
		goto bailout;
	}

	size = EK5SIZE;
	if (uri[KEY1SIZE] == ',') {
		if (0 != (rc = base64_decode(key2, &size, uri+KEY1SIZE+1))) {
			goto bailout;
		}
	}

	memcpy(key->sha1.digest, key1, sizeof(key->sha1.digest));
	key->log2size = key1[SHA1SIZE];
	key->type[0] = key1[SHA1SIZE+1];
	key->type[1] = key1[SHA1SIZE+1+1];
	memcpy(key->ek5.digest, key2, sizeof(key->ek5.digest));

bailout:
	return rc;
}

/**
 * @brief Check and decode a base64 encoded SSK (sub space key).
 *
 * Convert a base64 encoded SSK into the internal representation.
 * The string uri may be prepended by a protocol specifier
 * URI_PROTO or the alternative URI_PROTO_ALT.
 * The function detects public SSKs by their extension, which
 * is either BMCA or, for the sake of Freenet compatibility, PagM.
 * The four characters are just the base64 representation of the
 * key type and log2size.
 *
 * @param key pointer to a key to be set
 * @param uri pointer to a string containing the key in base64 representation.
 *
 * @result zero on success, non-zero on error (decoding base64)
 */
int str_ssk(chkey_t *key, const char *uri)
{
	uint8_t key1[SHA1SIZE+1+2];
	size_t size;
	int rc = 0;
	FUN("str_ssk");

	if (0 == strncmp(uri, URI_PROTO, strlen(URI_PROTO))) {
		uri += strlen(URI_PROTO);
	} else if (0 == strncmp(uri, URI_PROTO_ALT, strlen(URI_PROTO_ALT))) {
		uri += strlen(URI_PROTO_ALT);
	}
	if (0 == strncmp(uri, URI_CHK, strlen(URI_CHK))) {
		uri += strlen(URI_CHK);
	}
	if (0 == strncmp(uri, URI_SSK, strlen(URI_SSK))) {
		uri += strlen(URI_SSK);
	}

	memset(key1, 0, sizeof(key1));
	rc = base64_decode(key1, &size, uri);
	LOG(L_MINOR,("base64_decode() size %u; log2size:%x, keytype:%02x%02x\n",
		(unsigned)size, key1[SHA1SIZE+0], key1[SHA1SIZE+1], key1[SHA1SIZE+2]));
	memcpy(key->sha1.digest, key1, SHA1SIZE);
	key->log2size = key1[SHA1SIZE+0];
	key->type[0] = key1[SHA1SIZE+1];
	key->type[1] = key1[SHA1SIZE+2];
	/* detect PAgM SSK public keys */
	if (key->log2size == 0x3e &&
		key->type[0] == 0x00 &&
		key->type[1] == 0xc8) {
		LOG(L_MINOR,("fake Freenet public SSK@ detected\n"));
		key->log2size = log2size(2*SHA1SIZE);
		key->type[0] = MSB(K_SSK_P);
		key->type[1] = LSB(K_SSK_P);
	} else if (key->log2size != log2size(2*SHA1SIZE) ||
		key->type[0] != MSB(K_SSK_P) ||
		key->type[1] != LSB(K_SSK_P)) {
		key->log2size = log2size(SHA1SIZE);
		key->type[0] = MSB(K_SSK_S);
		key->type[1] = LSB(K_SSK_S);
	}
	memset(key->ek5.digest, 0, sizeof(key->ek5.digest));

	return rc;
}

/**
 * @brief Create the public SSK for a private SSK
 *
 * Create a public key from a private key,
 * simply by hashing the hex string of the SHA1 digest.
 *
 * @param key pointer to a key to receive the public SSK
 * @param ssk pointer to a key containing a private SSK
 *
 * @result zero on success
 */
int key_ssk_pub_from_priv(chkey_t *key, const chkey_t *ssk)
{
	char hex[SHA1SIZE*2+1];
	sha1_state_t sha1;
	size_t size;
	FUN("key_ssk_pub_from_priv");

	size = pm_snprintf(hex, sizeof(hex), "%s",
		sha1_hexstr(&ssk->sha1));

	sha1_init(&sha1);
	sha1_append(&sha1, hex, size);
	sha1_finish(&sha1, &key->sha1);

	key->type[0] = MSB(K_SSK_P);
	key->type[1] = LSB(K_SSK_P);
	key->log2size = log2size(size);

	memset(&key->ek5, 0, sizeof(key->ek5));

	return 0;
}

/**
 * @brief Create a private / public SSK pair.
 *
 * Create a private / public SSK pair. The private part is simply
 * a random number of size SHA1SIZE.
 * Hint: Sub space keys are not signed keys, they are just a
 * way to divide the name space into user domains.
 *
 * @param pub pointer to a key to receive the public key
 * @param priv pointer to a key to receive the private key
 *
 * @result zero on success
 */
int key_svk_pair_create(chkey_t *pub, chkey_t *priv)
{
	memset(priv, 0, sizeof(chkey_t));
	rnd_get(priv->sha1.digest, SHA1SIZE);
	priv->log2size = log2size(SHA1SIZE);
	priv->type[0] = MSB(K_SSK_S);
	priv->type[1] = LSB(K_SSK_S);
	return key_ssk_pub_from_priv(pub, priv);
}


/**
 * @brief Fixed sbox1 of the dragon cipher
 */
static const uint32_t sbox1[256] = {
	0x393BCE6B,0x232BA00D,0x84E18ADA,0x84557BA7,
	0x56828948,0x166908F3,0x414A3437,0x7BB44897,
	0x2315BE89,0x7A01F224,0x7056AA5D,0x121A3917,
	0xE3F47FA2,0x1F99D0AD,0x9BAD518B,0x99B9E75F,
	0x8829A7ED,0x2C511CA9,0x1D89BF75,0xF2F8CDD0,
	0x2DA2C498,0x48314C42,0x922D9AF6,0xAA6CE00C,
	0xAC66E078,0x7D4CB0C0,0x5500C6E8,0x23E4576B,
	0x6B365D40,0xEE171139,0x336BE860,0x5DBEEEFE,
	0x0E945776,0xD4D52CC4,0x0E9BB490,0x376EB6FD,
	0x6D891655,0xD4078FEE,0xE07401E7,0xA1E4350C,
	0xABC78246,0x73409C02,0x24704A1F,0x478ABB2C,
	0xA0849634,0x9E9E5FEB,0x77363D8D,0xD350BC21,
	0x876E1BB5,0xC8F55C9D,0xD112F39F,0xDF1A0245,
	0x9711B3F0,0xA3534F64,0x42FB629E,0x15EAD26A,
	0xD1CFA296,0x7B445FEE,0x88C28D4A,0xCA6A8992,
	0xB40726AB,0x508C65BC,0xBE87B3B9,0x4A894942,
	0x9AEECC5B,0x6CA6F10B,0x303F8934,0xD7A8693A,
	0x7C8A16E4,0xB8CF0AC9,0xAD14B784,0x819FF9F0,
	0xF20DCDFA,0xB7CB7159,0x58F3199F,0x9855E43B,
	0x1DF6C2D6,0x46114185,0xE46F5D0F,0xAAC70B5B,
	0x48590537,0x0FD77B28,0x67D16C70,0x75AE53F4,
	0xF7BFECA1,0x6017B2D2,0xD8A0FA28,0xB8FC2E0D,
	0x80168E15,0x0D7DEC9D,0xC5581F55,0xBE4A2783,
	0xD27012FE,0x53EA81CA,0xEBAA07D2,0x54F5D41D,
	0xABB26FA6,0x41B9EAD9,0xA48174C7,0x1F3026F0,
	0xEFBADD8E,0x387E9014,0x1505AB79,0xEADF0DF7,
	0x67755401,0xDA2EF962,0x41670B0E,0x0E8642F2,
	0xCE486070,0xA47D3312,0x4D7343A7,0xECDA58D0,
	0x1F79D536,0xD362576B,0x9D3A6023,0xC795A610,
	0xAE4DF639,0x60C0B14E,0xC6DD8E02,0xBDE93F4E,
	0xB7C3B0FF,0x2BE6BCAD,0xE4B3FDFD,0x79897325,
	0x3038798B,0x08AE6353,0x7D1D20EB,0x3B208D21,
	0xD0D6D104,0xC5244327,0x9893F59F,0xE976832A,
	0xB1EB320B,0xA409D915,0x7EC6B543,0x66E54F98,
	0x5FF805DC,0x599B223F,0xAD78B682,0x2CF5C6E8,
	0x4FC71D63,0x08F8FED1,0x81C3C49A,0xE4D0A778,
	0xB5D369CC,0x2DA336BE,0x76BC87CB,0x957A1878,
	0xFA136FBA,0x8F3C0E7B,0x7A1FF157,0x598324AE,
	0xFFBAAC22,0xD67DE9E6,0x3EB52897,0x4E07E855,
	0x87CE73F5,0x8D046706,0xD42D18F2,0xE71B1727,
	0x38473B38,0xB37B24D5,0x381C6AE1,0xE77D6589,
	0x6018CBFF,0x93CF3752,0x9B6EA235,0x504A50E8,
	0x464EA180,0x86AFBE5E,0xCC2D6AB0,0xAB91707B,
	0x1DB4D579,0xF9FAFD24,0x2B28CC54,0xCDCFD6B3,
	0x68A30978,0x43A6DFD7,0xC81DD98E,0xA6C2FD31,
	0x0FD07543,0xAFB400CC,0x5AF11A03,0x2647A909,
	0x24791387,0x5CFB4802,0x88CE4D29,0x353F5F5E,
	0x7038F851,0xF1F1C0AF,0x78EC6335,0xF2201AD1,
	0xDF403561,0x4462DFC7,0xE22C5044,0x9C829EA3,
	0x43FD6EAE,0x7A42B3A7,0x5BFAAAEC,0x3E046853,
	0x5789D266,0xE1219370,0xB2C420F8,0x3218BD4E,
	0x84590D94,0xD51D3A8C,0xA3AB3D24,0x2A339E3D,
	0xFEE67A23,0xAF844391,0x17465609,0xA99AD0A1,
	0x05CA597B,0x6024A656,0x0BF05203,0x8F559DDC,
	0x894A1911,0x909F21B4,0x6A7B63CE,0xE28DD7E7,
	0x4178AA3D,0x4346A7AA,0xA1845E4C,0x166735F4,
	0x639CA159,0x58940419,0x4E4F177A,0xD17959B2,
	0x12AA6FFD,0x1D39A8BE,0x7667F5AC,0xED0CE165,
	0xF1658FD8,0x28B04E02,0x1FA480CF,0xD3FB6FEF,
	0xED336CCB,0x9EE3CA39,0x9F224202,0x2D12D6E8,
	0xFAAC50CE,0xFA1E98AE,0x61498532,0x03678CC0,
	0x9E85EFD7,0x3069CE1A,0xF115D008,0x4553AA9F,
	0x3194BE09,0xB4A9367D,0x0A9DFEEC,0x7CA002D6,
	0x8E53A875,0x965E8183,0x14D79DAC,0x0192B555
};

/**
 * @brief Fixed sbox2 of the dragon cipher
 */
static const uint32_t sbox2[256] = {
	0xA94BC384,0xF7A81CAE,0xAB84ECD4,0x00DEF340,
	0x8E2329B8,0x23AF3A22,0x23C241FA,0xAED8729E,
	0x2E59357F,0xC3ED78AB,0x687724BB,0x7663886F,
	0x1669AA35,0x5966EAC1,0xD574C543,0xDBC3F2FF,
	0x4DD44303,0xCD4F8D01,0x0CBF1D6F,0xA8169D59,
	0x87841E00,0x3C515AD4,0x708784D6,0x13EB675F,
	0x57592B96,0x07836744,0x3E721D90,0x26DAA84F,
	0x253A4E4D,0xE4FA37D5,0x9C0830E4,0xD7F20466,
	0xD41745BD,0x1275129B,0x33D0F724,0xE234C68A,
	0x4CA1F260,0x2BB0B2B6,0xBD543A87,0x4ABD3789,
	0x87A84A81,0x948104EB,0xA9AAC3EA,0xBAC5B4FE,
	0xD4479EB6,0xC4108568,0xE144693B,0x5760C117,
	0x48A9A1A6,0xA987B887,0xDF7C74E0,0xBC0682D7,
	0xEDB7705D,0x57BFFEAA,0x8A0BD4F1,0x1A98D448,
	0xEA4615C9,0x99E0CBD6,0x780E39A3,0xADBCD406,
	0x84DA1362,0x7A0E984B,0xBED853E6,0xD05D610B,
	0x9CAC6A28,0x1682ACDF,0x889F605F,0x9EE2FEBA,
	0xDB556C92,0x86818021,0x3CC5BEA1,0x75A934C6,
	0x95574478,0x31A92B9B,0xBFE3E92B,0xB28067AE,
	0xD862D848,0x0732A22D,0x840EF879,0x79FFA920,
	0x0124C8BB,0x26C75B69,0xC3DAAAC5,0x6E71F2E9,
	0x9FD4AFA6,0x474D0702,0x8B6AD73E,0xF5714E20,
	0xE608A352,0x2BF644F8,0x4DF9A8BC,0xB71EAD7E,
	0x6335F5FB,0x0A271CE3,0xD2B552BB,0x3834A0C3,
	0x341C5908,0x0674A87B,0x8C87C0F1,0xFF0842FC,
	0x48C46BDB,0x30826DF8,0x8B82CE8E,0x0235C905,
	0xDE4844C3,0x296DF078,0xEFAA6FEA,0x6CB98D67,
	0x6E959632,0xD5D3732F,0x68D95F19,0x43FC0148,
	0xF808C7B1,0xD45DBD5D,0x5DD1B83B,0x8BA824FD,
	0xC0449E98,0xB743CC56,0x41FADDAC,0x141E9B1C,
	0x8B937233,0x9B59DCA7,0xF1C871AD,0x6C678B4D,
	0x46617752,0xAAE49354,0xCABE8156,0x6D0AC54C,
	0x680CA74C,0x5CD82B3F,0xA1C72A59,0x336EFB54,
	0xD3B1A748,0xF4EB40D5,0x0ADB36CF,0x59FA1CE0,
	0x2C694FF9,0x5CE2F81A,0x469B9E34,0xCE74A493,
	0x08B55111,0xEDED517C,0x1695D6FE,0xE37C7EC7,
	0x57827B93,0x0E02A748,0x6E4A9C0F,0x4D840764,
	0x9DFFC45C,0x891D29D7,0xF9AD0D52,0x3F663F69,
	0xD00A91B9,0x615E2398,0xEDBBC423,0x09397968,
	0xE42D6B68,0x24C7EFB1,0x384D472C,0x3F0CE39F,
	0xD02E9787,0xC326F415,0x9E135320,0x150CB9E2,
	0xED94AFC7,0x236EAB0F,0x596807A0,0x0BD61C36,
	0xA29E8F57,0x0D8099A5,0x520200EA,0xD11FF96C,
	0x5FF47467,0x575C0B39,0x0FC89690,0xB1FBACE8,
	0x7A957D16,0xB54D9F76,0x21DC77FB,0x6DE85CF5,
	0xBFE7AEE9,0xC49571A9,0x7F1DE4DA,0x29E03484,
	0x786BA455,0xC26E2109,0x4A0215F4,0x44BFF99C,
	0x711A2414,0xFDE9CDD0,0xDCE15B77,0x66D37887,
	0xF006CB92,0x27429119,0xF37B9784,0x9BE182D9,
	0xF21B8C34,0x732CAD2D,0xAF8A6A60,0x33A5D3AF,
	0x633E2688,0x5EAB5FD1,0x23E6017A,0xAC27A7CF,
	0xF0FC5A0E,0xCC857A5D,0x20FB7B56,0x3241F4CD,
	0xE132B8F7,0x4BB37056,0xDA1D5F94,0x76E08321,
	0xE1936A9C,0x876C99C3,0x2B8A5877,0xEB6E3836,
	0x9ED8A201,0xB49B5122,0xB1199638,0xA0A4AF2B,
	0x15F50A42,0x775F3759,0x41291099,0xB6131D94,
	0x9A563075,0x224D1EB1,0x12BB0FA2,0xFF9BFC8C,
	0x58237F23,0x98EF2A15,0xD6BCCF8A,0xB340DC66,
	0x0D7743F0,0x13372812,0x6279F82B,0x4E45E519,
	0x98B4BE06,0x71375BAE,0x2173ED47,0x14148267,
	0xB7AB85B5,0xA875E314,0x1372F18D,0xFD105270,
	0xB83F161F,0x5C175260,0x44FFD49F,0xD428C4F6,
	0x2C2002FC,0xF2797BAF,0xA3B20A4E,0xB9BF1A89,
	0xE4ABA5E2,0xC912C58D,0x96516F9A,0x51561E77
};

/**
 * @brief Virtual 32x32 s-box G1
 */
#define G1(x, r) \
    r = sbox2[x % 256] ^ \
        sbox1[(x >> 8) % 256] ^ \
        sbox1[(x >> 16) % 256] ^ \
        sbox1[(x >> 24) % 256]

/**
 * @brief Virtual 32x32 s-box G2
 */
#define G2(x, r) \
    r = sbox1[x % 256] ^ \
        sbox2[(x >> 8) % 256] ^ \
        sbox1[(x >> 16) % 256] ^ \
        sbox1[(x >> 24) % 256]

/**
 * @brief Virtual 32x32 s-box G3
 */
#define G3(x, r) \
    r = sbox1[x % 256] ^ \
        sbox1[(x >> 8) % 256] ^ \
        sbox2[(x >> 16) % 256] ^ \
        sbox1[(x >> 24) % 256]

/**
 * @brief Virtual 32x32 s-box H1
 */
#define H1(x, r) \
    r = sbox1[x % 256] ^  \
        sbox2[(x >> 8) % 256] ^ \
        sbox2[(x >> 16) % 256] ^ \
        sbox2[(x >> 24) % 256]

/**
 * @brief Virtual 32x32 s-box H2
 */
#define H2(x, r) \
    r = sbox2[x % 256] ^  \
        sbox1[(x >> 8) % 256] ^ \
        sbox2[(x >> 16) % 256] ^ \
        sbox2[(x >> 24) % 256]

/**
 * @brief Virtual 32x32 s-box H3
 */
#define H3(x, r) \
    r = sbox2[x % 256] ^  \
        sbox2[(x >> 8) % 256] ^ \
        sbox1[(x >> 16) % 256] ^ \
        sbox2[(x >> 24) % 256]

/**
 * @brief Index into 32 word state, using offset
 */
#define STATE(ctx, offset) \
       ctx.d[(ctx.i + offset) % 32]

/**
 * @brief The update function F
 */
#define DUPDATE(a, b, c, d, e, f, t) do { \
       b ^= a; d ^= c; f ^= e; \
       c += b; e += d; a += f; \
       G1(a, t); d ^= t; \
       G2(c, t); f ^= t; \
       G3(e, t); b ^= t; \
       H1(b, t); a ^= t; \
       H2(d, t); c ^= t; \
       H3(f, t); e ^= t; \
       f += c; b += e; d += a; \
       a ^= f; c ^= b; e ^= d; \
} while (0)

/**
 * @brief Generator function
 */
#define DGEN(ctx, a, b, c, d, e, f, t) \
       a  = STATE(ctx, 0); \
       b  = STATE(ctx, 9); \
       c  = STATE(ctx, 16); \
       d  = STATE(ctx, 19); \
       e  = STATE(ctx, 30) ^ ((uint32_t)(ctx.m >> 32)); \
       f  = STATE(ctx, 31) ^ ((uint32_t)ctx.m); \
       DUPDATE(a, b, c, d, e, f, t); \
       ctx.m += 1; \
       ctx.i -= 2; \
       STATE(ctx, 0) = b; \
       STATE(ctx, 1) = c; \

/**
 * @brief Initialisation for 128-bit key and 128-bit IV
 */
#define DINIT128(ctx, key, iv, a, b, c, d, e, f, t, j) do { \
	ctx.i = 0; \
	e = 0x00004472; \
	f = 0x61676F6E; \
	for (j = 0; j < 4; j++) { \
		STATE(ctx, j)      = key[j]; \
		STATE(ctx, j + 4)  = key[(j + 2) % 4] ^ iv[(j + 2) % 4]; \
		STATE(ctx, j + 8)  = iv[j]; \
		STATE(ctx, j + 12) = key[j] ^ iv[(j + 2) % 4]; \
		STATE(ctx, j + 16) = key[(j + 2)  % 4]; \
		STATE(ctx, j + 20) = key[j] ^ iv[j]; \
		STATE(ctx, j + 24) = iv[(j + 2) % 4]; \
		STATE(ctx, j + 28) = key[(j + 2) % 4] ^ iv[j]; \
	} \
	for (j = 0; j < 16; j++) { \
		a  = STATE(ctx, 0) ^ STATE(ctx, 24) ^ STATE(ctx, 28); \
		b  = STATE(ctx, 1) ^ STATE(ctx, 25) ^ STATE(ctx, 29); \
		c  = STATE(ctx, 2) ^ STATE(ctx, 26) ^ STATE(ctx, 30); \
		d  = STATE(ctx, 3) ^ STATE(ctx, 27) ^ STATE(ctx, 31); \
		DUPDATE(a, b, c, d, e, f, t); \
		ctx.i -= 4; \
		STATE(ctx, 0) = a ^ STATE(ctx, 20); \
		STATE(ctx, 1) = b ^ STATE(ctx, 21); \
		STATE(ctx, 2) = c ^ STATE(ctx, 22); \
		STATE(ctx, 3) = d ^ STATE(ctx, 23); \
	} \
	ctx.m = ((((uint64_t)e) << 32) | ((uint64_t)f)); \
} while (0)


/**
 * @brief Fixed IV for Entropy
 */
static const uint32_t iv1[4] = {
	0x01234567, 0x89abcdef,
	0x19740705, 0x20050620
};

/**
 * @brief Allocate and setup key context from a EK5
 *
 * Allocate space for a dragon cipher and initialize it from
 * the Entropy specific 128 bits EK5 hash pointed to by key.
 * Note: This variant is just used in the test target crypt,
 * the regular code uses the 'all-in-one' function key_crypt.
 *
 * @param key pointer to an EK5 hash
 *
 * @result pointer to the allocated dragon cipher context
 */
dragon_t *key_setup(uint8_t *key)
{
	dragon_t dctx, *ctx;
	uint32_t u32[4];
	uint32_t a, b, c, d, e, f, t, j;

	u32[0] = ntohl(*(uint32_t*)&key[ 0]);
	u32[1] = ntohl(*(uint32_t*)&key[ 4]);
	u32[2] = ntohl(*(uint32_t*)&key[ 8]);
	u32[3] = ntohl(*(uint32_t*)&key[12]);

	DINIT128(dctx, u32, iv1, a, b, c, d, e, f, t, j);

	ctx = (dragon_t *)xmalloc(sizeof(dragon_t));
	*ctx = dctx;
	memset(&dctx, 0, sizeof(dctx));
	u32[0] = u32[1] = u32[2] = u32[3] = 0;
	a = b = c = d = e = f = t = 0;
	return ctx;
}

/**
 * @brief Produce more output for the dragon cipher
 *
 * Produce a number of outputs for the dragon cipher.
 * Note: The size must be divisible by 8. This code
 * is just used in the test target crypt.
 *
 * @param ctx pointer to a dragon cipher context
 * @param buff pointer to the receiving buffer
 * @param size size of the receiving buffer in bytes
 *
 * @result zero on success
 */
int key_output(dragon_t *ctx, void *buff, size_t size)
{
	dragon_t dctx;
	uint32_t a, b, c, d, e, f, t, *p;
	size_t i;

	dctx = *ctx;
	p = buff;
	for (i = 0; i < size; i += 8) {
		DGEN(dctx, a, b, c, d, e, f, t);
		*p++ = htonl(a);
		*p++ = htonl(e);
	}
	*ctx = dctx;
	memset(&dctx, 0, sizeof(dctx));
	a = b = c = d = e = f = t = 0;
	return 0;
}

/**
 * @brief Destroy and free a dragon cipher context
 *
 * Note: This code is just used in the test target crypt.
 *
 * @param ctx pointer to a dragon cipher context
 *
 * @result zero on success
 */
int key_destroy(dragon_t *ctx)
{
	memset(ctx, 0, sizeof(*ctx));
	xfree(ctx);
	return 0;
}

/**
 * @brief Crypt a (memory mapped) buffer using the given key.
 *
 * This is the workhorse for key contents encryption an decryption.
 * The dragon cipher context is allocated from the stack, and
 * destroyed at the end of the function.
 *
 * @param key pointer to the EK5 hash used as seed
 * @param buff pointer to the (memory mapped) byte array
 * @param size size of the byte array in bytes
 * @param flags flags specifying if a header should be skipped
 *
 * @result zero on success
 */
int key_crypt(uint8_t *key, void *buff, size_t size, int flags)
{
	uint8_t *pb = (uint8_t *)buff;
	dragon_t dctx;
	uint32_t a, b, c, d, e, f, t;
	uint32_t u32[4];
	size_t i, j;
	FUN("key_crypt");

	u32[0] = ntohl(*(uint32_t*)&key[ 0]);
	u32[1] = ntohl(*(uint32_t*)&key[ 4]);
	u32[2] = ntohl(*(uint32_t*)&key[ 8]);
	u32[3] = ntohl(*(uint32_t*)&key[12]);

	DINIT128(dctx, u32, iv1, a, b, c, d, e, f, t, j);

	if (0 == (flags & KEY_CRYPT_GZ)) {
		/* Crypt a complete buffer (e.g. a file fragment) */
		for (i = 0; i < size; i++) {
			switch (i % 8) {
			case 0:
				DGEN(dctx, a, b, c, d, e, f, t);
				if (i + 8 <= size) {
					*(uint32_t*)pb ^= htonl(a);
					pb += 4;
					*(uint32_t*)pb ^= htonl(e);
					pb += 4;
					i += 7;
					continue;
				}
				*pb++ ^= (uint8_t)(a >> 24);
				break;
			case 1:
				*pb++ ^= (uint8_t)(a >> 16);
				break;
			case 2:
				*pb++ ^= (uint8_t)(a >> 8);
				break;
			case 3:
				*pb++ ^= (uint8_t)a;
				break;
			case 4:
				*pb++ ^= (uint8_t)(e >> 24);
				break;
			case 5:
				*pb++ ^= (uint8_t)(e >> 16);
				break;
			case 6:
				*pb++ ^= (uint8_t)(e >> 8);
				break;
			case 7:
				*pb++ ^= (uint8_t)e;
				break;
			}
		}
	} else {
		/*
		 * Crypt a gzip compressed buffer:
		 * skip 4 bytes version header
		 * skip 4 bytes compressed size of the block
		 * skip 4 bytes uncompressed size
		 */
		pb += 3 * 4;
		for (i = 0; i < size - 3 * 4; i++) {
			switch (i % 8) {
			case 0:
				DGEN(dctx, a, b, c, d, e, f, t);
				if (i + 8 <= size - 3 * 4) {
					*(uint32_t*)pb ^= htonl(a);
					pb += 4;
					*(uint32_t*)pb ^= htonl(e);
					pb += 4;
					i += 7;
					continue;
				}
				*pb++ ^= (uint8_t)(a >> 24);
				break;
			case 1:
				*pb++ ^= (uint8_t)(a >> 16);
				break;
			case 2:
				*pb++ ^= (uint8_t)(a >> 8);
				break;
			case 3:
				*pb++ ^= (uint8_t)a;
				break;
			case 4:
				*pb++ ^= (uint8_t)(e >> 24);
				break;
			case 5:
				*pb++ ^= (uint8_t)(e >> 16);
				break;
			case 6:
				*pb++ ^= (uint8_t)(e >> 8);
				break;
			case 7:
				*pb++ ^= (uint8_t)e;
				break;
			}
		}
	}
	memset(&dctx, 0, sizeof(dctx));
	u32[0] = u32[1] = u32[2] = u32[3] = 0;
	a = b = c = d = e = f = t = 0;
	i = j = 0;

	return 0;
}
